﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using ActiproSoftware.SyntaxEditor;

namespace WindowsUserControl.Codes
{
    /// <summary>
    /// 
    /// </summary>
    public class SimpleSyntaxLanguage:MergableSyntaxLanguage, ISemanticParserServiceProcessor 
    {
        private SimpleLexicalParser lexicalParser = new SimpleLexicalParser(true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes a new instance of the <c>SimpleSyntaxLanguage</c> class.
        /// </summary>
        public SimpleSyntaxLanguage()
            : base("Simple")
        {
            this.ExampleText = @"/*
	The SIMPLE language example demonstrates a C-like
	language that only knows several keywords
	and operators.  All variables are integer numbers.
	See the grammar definition for EBNF notation
	of the semantic grammar.
	
	This sample is intended to provide a simple base 
	for implementing your own advanced language 
	add-on.  It even demonstrates a high-level 
	implementation of IntelliPrompt for function
	names, parameters, and keywords.
*/

function Add(x, y) {
	return x + y;
}

function Increment(x) {
	return (x + 1);
}

function IncrementAndMultiply(x, y) {
	// This function calls another function
	var result;
	result = Increment(x);
	return result * y;
}
";

            // Initialize highlighting styles
            this.HighlightingStyles.Add(new HighlightingStyle("KeywordStyle", null, Color.Blue, Color.Empty));
            this.HighlightingStyles.Add(new HighlightingStyle("CommentStyle", null, Color.Green, Color.Empty));
            this.HighlightingStyles.Add(new HighlightingStyle("NumberStyle", null, Color.Purple, Color.Empty));

            // Initialize lexical states
            this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.Default, "DefaultState"));
            this.DefaultLexicalState = this.LexicalStates["DefaultState"];
            this.LexicalStates["DefaultState"].DefaultHighlightingStyle = this.HighlightingStyles["DefaultStyle"];
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // INTERFACE IMPLEMENTATION
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Performs a semantic parsing operation using the data in the <see cref="SemanticParserServiceRequest"/>.
        /// </summary>
        /// <param name="request">A <see cref="SemanticParserServiceRequest"/> containing data about what to parse.</param>
        void ISemanticParserServiceProcessor.Process(SemanticParserServiceRequest request)
        {
            request.SemanticParseData = MergableLexicalParserManager.PerformSemanticParse(this, request.TextBufferReader, request.Filename) as ISemanticParseData;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns the quick info for the <see cref="SyntaxEditor"/> at the specified offset.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> to examine.</param>
        /// <param name="offset">The offset to examine.  The offset is updated to the start of the context.</param>
        /// <returns>The quick info for the <see cref="SyntaxEditor"/> at the specified offset.</returns>
        private string GetQuickInfo(SyntaxEditor syntaxEditor, ref int offset)
        {
            // Get the identifier at the offset, if any
            TextStream stream = syntaxEditor.Document.GetTextStream(offset);
            if (!stream.IsAtTokenStart)
                stream.GoToCurrentTokenStart();
            offset = stream.Offset;
            if (stream.Token.ID != SimpleTokenID.Identifier)
                return null;
            string text = stream.TokenText;

            // Get the containing node
            CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
            AstNode containingNode = null;
            if (compilationUnit != null)
                containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

            if (containingNode != null)
            {
                // Get the containing function
                FunctionDeclaration containingFunctionDeclaration = containingNode.FindAncestor(typeof(FunctionDeclaration)) as FunctionDeclaration;
                if ((containingFunctionDeclaration != null) && (!containingFunctionDeclaration.BlockStatement.Contains(stream.Offset)))
                    containingFunctionDeclaration = null;

                // Check for a function parameter
                if (containingFunctionDeclaration != null)
                {
                    foreach (Identifier parameterName in containingFunctionDeclaration.Parameters)
                    {
                        if (parameterName.Text == text)
                            return this.GetQuickInfoForFunctionParameter(parameterName, true);
                    }
                }

                // Check for a function name
                foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions)
                {
                    if (functionDeclaration.Name.Text == text)
                        return this.GetQuickInfoForFunctionDeclaration(functionDeclaration, -1, true);
                }
            }

            return null;
        }

        /// <summary>
        /// Gets the formatted text to display in quick info for the specified <see cref="FunctionDeclaration"/>.
        /// </summary>
        /// <param name="functionDeclaration">The <see cref="FunctionDeclaration"/> to examine.</param>
        /// <param name="parameterIndex">The index of the selected parameter.</param>
        /// <param name="reflectionIconsEnabled">Whether reflection icons are enabled.</param>
        /// <returns>The formatted text to display in quick info for the specified <see cref="FunctionDeclaration"/>.</returns>
        private string GetQuickInfoForFunctionDeclaration(FunctionDeclaration functionDeclaration, int parameterIndex, bool reflectionIconsEnabled)
        {
            StringBuilder result = new StringBuilder();
            if (reflectionIconsEnabled)
                result.Append("<img src=\"resource:PublicMethod\" align=\"absbottom\"/> ");
            result.Append("<span style=\"color: blue;\">function</span> ");
            result.Append(String.Format("<span style=\"color: teal;\">{0}</span>", IntelliPrompt.EscapeMarkupText(functionDeclaration.Name.Text)));
            result.Append("(");
            IAstNodeList parameters = functionDeclaration.Parameters;
            for (int index = 0; index < parameters.Count; index++)
            {
                if (index > 0)
                    result.Append(", ");
                if (parameterIndex == index)
                    result.Append("<b>");
                result.Append(IntelliPrompt.EscapeMarkupText(((Identifier)parameters[index]).Text));
                if (parameterIndex == index)
                    result.Append("</b>");
            }
            result.Append(")<br/>");
            result.Append(String.Format("<span style=\"color: green;\">This function has {0} parameter(s).</span>", parameters.Count));
            return result.ToString();
        }

        /// <summary>
        /// Gets the formatted text to display in quick info for the specified function parameter.
        /// </summary>
        /// <param name="parameterName">The <see cref="Identifier"/> to examine.</param>
        /// <param name="reflectionIconsEnabled">Whether reflection icons are enabled.</param>
        /// <returns>The formatted text to display in quick info for the specified function parameter.</returns>
        private string GetQuickInfoForFunctionParameter(Identifier parameterName, bool reflectionIconsEnabled)
        {
            return String.Format("<i style=\"color: gray;\">(parameter)</i> {0}<b>{1}</b><br/><span style=\"color: green;\">Parameter is of function {2}.</span>",
                (reflectionIconsEnabled ? @"<img src=""resource:PublicField"" align=""absbottom""/> " : String.Empty),
                IntelliPrompt.EscapeMarkupText(parameterName.Text),
                IntelliPrompt.EscapeMarkupText(((FunctionDeclaration)parameterName.ParentNode).Name.Text)
                );
        }

        private string GetQuickInfoForKeyword1(string keyword)
        {
            return String.Format("<i style=\"color: gray;\">(keyword)</i> <b>{0}</b>", IntelliPrompt.EscapeMarkupText(keyword));
        }

        private bool ShowIntelliPromptMemberList(SyntaxEditor syntaxEditor, bool completeWord)
        {
            // Try and ensure the compilation unit is up-to-date
            SemanticParserService.WaitForParse(SemanticParserServiceRequest.GetParseHashKey(syntaxEditor.Document, syntaxEditor.Document));

            // Initialize the member list
            IntelliPromptMemberList memberList = syntaxEditor.IntelliPrompt.MemberList;
            memberList.ResetAllowedCharacters();
            memberList.Clear();
            memberList.ImageList = SyntaxEditor.ReflectionImageList;

            // Get the target text range
            TextRange targetTextRange = TextRange.Deleted;
            TextStream stream = syntaxEditor.Document.GetTextStream(syntaxEditor.Caret.Offset);
            if (stream.IsAtTokenStart)
            {
                if (stream.Offset > 0)
                {
                    stream.GoToPreviousToken();
                    if (stream.Token.ID == SimpleTokenID.Identifier)
                        targetTextRange = stream.Token.TextRange;
                }
            }
            else
            {
                stream.GoToCurrentTokenStart();
                targetTextRange = stream.Token.TextRange;
            }

            // Get the containing node
            CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
            AstNode containingNode = null;
            if (compilationUnit != null)
                containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

            // Get the member list items
            Hashtable memberListItemHashtable = new Hashtable();

            // Add keywords
            if (containingNode == null)
                memberListItemHashtable["function"] = new IntelliPromptMemberListItem("function", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
            else
            {
                memberListItemHashtable["return"] = new IntelliPromptMemberListItem("return", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
                memberListItemHashtable["var"] = new IntelliPromptMemberListItem("var", (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword);
            }

            // Add functions 
            if ((compilationUnit != null) && (containingNode != null))
            {
                foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions)
                    memberListItemHashtable[functionDeclaration.Name.Text] = new IntelliPromptMemberListItem(functionDeclaration.Name.Text,
                        (int)ActiproSoftware.Products.SyntaxEditor.IconResource.PublicMethod, functionDeclaration);
            }

            if (memberListItemHashtable.Count > 0)
            {
                IntelliPromptMemberListItem[] items = new IntelliPromptMemberListItem[memberListItemHashtable.Count];
                memberListItemHashtable.Values.CopyTo(items, 0);
                memberList.AddRange(items);
            }

            // Show the list
            if (memberList.Count > 0)
            {
                if (targetTextRange.IsDeleted)
                    memberList.Show();
                else if (completeWord)
                    memberList.CompleteWord(targetTextRange.StartOffset, targetTextRange.Length);
                else
                    memberList.Show(targetTextRange.StartOffset, targetTextRange.Length);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Provides the core functionality to shows parameter info based on the current context in a <see cref="SyntaxEditor"/>.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the parameter info.</param>
        /// <param name="offset">The offset to examine.</param>
        /// <returns>
        /// <c>true</c> if parameter info is displayed; otherwise, <c>false</c>.
        /// </returns>
        private bool ShowParameterInfoCore(SyntaxEditor syntaxEditor, int offset)
        {
            // Initialize the parameter info
            syntaxEditor.IntelliPrompt.ParameterInfo.Hide();
            syntaxEditor.IntelliPrompt.ParameterInfo.Info.Clear();
            syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex = 0;

            // Get the identifier at the offset, if any
            TextStream stream = syntaxEditor.Document.GetTextStream(offset);
            if (!stream.IsAtTokenStart)
                stream.GoToCurrentTokenStart();
            stream.GoToPreviousToken();
            if (stream.Token.ID != SimpleTokenID.OpenParenthesis)
                return false;
            stream.GoToPreviousToken();
            if (stream.Token.ID != SimpleTokenID.Identifier)
                return false;
            string text = stream.TokenText;

            // Get the containing node
            CompilationUnit compilationUnit = syntaxEditor.Document.SemanticParseData as CompilationUnit;
            AstNode containingNode = null;
            if (compilationUnit != null)
                containingNode = compilationUnit.FindNodeRecursive(stream.Offset) as AstNode;

            if (containingNode != null)
            {
                // Get the containing function
                FunctionDeclaration containingFunctionDeclaration = containingNode.FindAncestor(typeof(FunctionDeclaration)) as FunctionDeclaration;
                if ((containingFunctionDeclaration != null) && (!containingFunctionDeclaration.BlockStatement.Contains(stream.Offset)))
                    containingFunctionDeclaration = null;

                if (containingFunctionDeclaration != null)
                {
                    foreach (FunctionDeclaration functionDeclaration in compilationUnit.Functions)
                    {
                        if (functionDeclaration.Name.Text == text)
                        {
                            IAstNodeList parameters = functionDeclaration.Parameters;
                            if (parameters.Count > 0)
                            {
                                // Determine the parameter text range
                                TextRange parameterTextRange = new TextRange(offset);
                                stream.Offset = offset - 1;
                                if (stream.GoToNextMatchingToken(stream.Token))
                                    parameterTextRange = new TextRange(offset, stream.Offset + 1);
                                else
                                    parameterTextRange = new TextRange(offset, offset + 1);

                                // Configure the parameter info
                                syntaxEditor.IntelliPrompt.ParameterInfo.ValidTextRange = parameterTextRange;
                                syntaxEditor.IntelliPrompt.ParameterInfo.CloseDelimiterCharacter = ')';

                                // Update the parameter index
                                syntaxEditor.IntelliPrompt.ParameterInfo.UpdateParameterIndex();

                                // Add an item
                                syntaxEditor.IntelliPrompt.ParameterInfo.Info.Add(this.GetQuickInfoForFunctionDeclaration(functionDeclaration, syntaxEditor.IntelliPrompt.ParameterInfo.ParameterIndex, true));

                                // Store the function declaration in the context
                                syntaxEditor.IntelliPrompt.ParameterInfo.Context = functionDeclaration;

                                // Show the parameter info
                                syntaxEditor.IntelliPrompt.ParameterInfo.Show(offset);
                                return true;
                            }
                            break;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Updates the parameter info's selected item's text.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> to examine.</param>
        /// <param name="force">Whether to force the update.  Otherwise the update only occurs if the text is not yet initialized.</param>
        /// <returns>
        /// <c>true</c> if an update occurred; otherwise, <c>false</c>.
        /// </returns>
        private bool UpdateParameterInfoSelectedText(SyntaxEditor syntaxEditor, bool force)
        {
            // If the parameter info is visible and there is a context available...
            if ((syntaxEditor.IntelliPrompt.ParameterInfo.Visible) && (syntaxEditor.IntelliPrompt.ParameterInfo.Context is FunctionDeclaration))
            {
                // Get the function declaration
                FunctionDeclaration functionDeclaration = (FunctionDeclaration)syntaxEditor.IntelliPrompt.ParameterInfo.Context;

                // If the parameter info item needs markup...
                if ((force) || (syntaxEditor.IntelliPrompt.ParameterInfo.Info[syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex] == null))
                {
                    // Update the parameter info item
                    syntaxEditor.IntelliPrompt.ParameterInfo.Info[syntaxEditor.IntelliPrompt.ParameterInfo.SelectedIndex] =
                        this.GetQuickInfoForFunctionDeclaration(functionDeclaration, syntaxEditor.IntelliPrompt.ParameterInfo.ParameterIndex, true);
                    return true;
                }
            }
            return false;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Resets the <see cref="AutomaticOutliningBehavior"/> property to its default value.
        /// </summary>
        public override void ResetAutomaticOutliningBehavior()
        {
            this.AutomaticOutliningBehavior = ActiproSoftware.SyntaxEditor.AutomaticOutliningBehavior.SemanticParseDataChange;
        }
        /// <summary>
        /// Indicates whether the <see cref="AutomaticOutliningBehavior"/> property should be persisted.
        /// </summary>
        /// <returns>
        /// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
        /// </returns>
        public override bool ShouldSerializeAutomaticOutliningBehavior()
        {
            return (this.AutomaticOutliningBehavior != ActiproSoftware.SyntaxEditor.AutomaticOutliningBehavior.SemanticParseDataChange);
        }

        /// <summary>
        /// Creates an <see cref="IToken"/> that represents the end of a document.
        /// </summary>
        /// <param name="startOffset">The start offset of the <see cref="IToken"/>.</param>
        /// <param name="lexicalState">The <see cref="ILexicalState"/> that contains the token.</param>
        /// <returns>An <see cref="IToken"/> that represents the end of a document.</returns>
        public override IToken CreateDocumentEndToken(int startOffset, ILexicalState lexicalState)
        {
            return new SimpleToken(startOffset, 0, LexicalParseFlags.None, null, new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)SimpleTokenID.DocumentEnd));
        }

        /// <summary>
        /// Creates an <see cref="IToken"/> that represents an invalid range of text.
        /// </summary>
        /// <param name="startOffset">The start offset of the <see cref="IToken"/>.</param>
        /// <param name="length">The length of the <see cref="IToken"/>.</param>
        /// <param name="lexicalState">The <see cref="ILexicalState"/> that contains the token.</param>
        /// <returns>An <see cref="IToken"/> that represents an invalid range of text.</returns>
        public override IToken CreateInvalidToken(int startOffset, int length, ILexicalState lexicalState)
        {
            return new SimpleToken(startOffset, length, LexicalParseFlags.None, null, new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)SimpleTokenID.Invalid));
        }

        /// <summary>
        /// Creates an <see cref="IToken"/> that represents the range of text with the specified lexical parse data.
        /// </summary>
        /// <param name="startOffset">The start offset of the token.</param>
        /// <param name="length">The length of the token.</param>
        /// <param name="lexicalParseFlags">The <see cref="LexicalParseFlags"/> for the token.</param>
        /// <param name="parentToken">The <see cref="IToken"/> that starts the current state scope specified by the <see cref="ITokenLexicalParseData.LexicalState"/> property.</param>
        /// <param name="lexicalParseData">The <see cref="ITokenLexicalParseData"/> that contains lexical parse information about the token.</param>
        /// <returns></returns>
        public override IToken CreateToken(int startOffset, int length, LexicalParseFlags lexicalParseFlags, IToken parentToken, ITokenLexicalParseData lexicalParseData)
        {
            return new SimpleToken(startOffset, length, lexicalParseFlags, parentToken, lexicalParseData);
        }

        /// <summary>
        /// </summary>
        public override void ResetErrorDisplayEnabled()
        {
            this.ErrorDisplayEnabled = true;
        }
        /// <summary>
        /// </summary>
        /// <returns>
        /// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
        /// </returns>
        public override bool ShouldSerializeErrorDisplayEnabled()
        {
            return !this.ErrorDisplayEnabled;
        }

        /// <summary>
        /// Returns the <see cref="HighlightingStyle"/> for the specified <see cref="IToken"/>.
        /// </summary>
        /// <param name="token">The <see cref="IToken"/> to examine.</param>
        /// <returns>The <see cref="HighlightingStyle"/> for the specified <see cref="IToken"/>.</returns>
        public override HighlightingStyle GetHighlightingStyle(IToken token)
        {
            switch (token.ID)
            {
                case SimpleTokenID.SingleLineComment:
                case SimpleTokenID.MultiLineComment:
                    return this.HighlightingStyles["CommentStyle"];
                case SimpleTokenID.Number:
                    return this.HighlightingStyles["NumberStyle"];
                default:
                    if ((token.ID > SimpleTokenID.KeywordStart) && (token.ID < SimpleTokenID.KeywordEnd))
                        return this.HighlightingStyles["KeywordStyle"];
                    else
                        return token.LexicalState.DefaultHighlightingStyle;
            }
        }

        /// <summary>
        /// Gets the token string representation for the specified token ID.
        /// </summary>
        /// <param name="tokenID">The ID of the token to examine.</param>
        /// <returns>The token string representation for the specified token ID.</returns>
        public override string GetTokenString(int tokenID)
        {
            if ((tokenID > (int)SimpleTokenID.KeywordStart) && (tokenID < (int)SimpleTokenID.KeywordEnd))
                return SimpleTokenID.GetTokenKey(tokenID).ToLower();

            switch (tokenID)
            {
                // General
                case SimpleTokenID.DocumentEnd:
                    return "Document end";
                case SimpleTokenID.Whitespace:
                    return "Whitespace";
                case SimpleTokenID.LineTerminator:
                    return "Line terminator";
                case SimpleTokenID.SingleLineComment:
                    return "Single-line comment";
                case SimpleTokenID.MultiLineComment:
                    return "Multi-line comment";
                case SimpleTokenID.Number:
                    return "Number";
                case SimpleTokenID.Identifier:
                    return "Identifier";
                // Operators
                case SimpleTokenID.OpenCurlyBrace:
                    return "{";
                case SimpleTokenID.CloseCurlyBrace:
                    return "}";
                case SimpleTokenID.OpenParenthesis:
                    return "(";
                case SimpleTokenID.CloseParenthesis:
                    return ")";
                case SimpleTokenID.Comma:
                    return ",";
                case SimpleTokenID.SemiColon:
                    return ";";
                case SimpleTokenID.Addition:
                    return "+";
                case SimpleTokenID.Subtraction:
                    return "-";
                case SimpleTokenID.Multiplication:
                    return "*";
                case SimpleTokenID.Division:
                    return "/";
                case SimpleTokenID.Assignment:
                    return "=";
                case SimpleTokenID.Equality:
                    return "==";
                case SimpleTokenID.Inequality:
                    return "!=";
            }

            return null;
        }

        /// <summary>
        /// Performs an auto-complete if the <see cref="SyntaxEditor"/> context with which the IntelliPrompt member list is initialized causes a single selection.
        /// Otherwise, displays a member list in the <see cref="SyntaxEditor"/>.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt member list.</param>
        /// <returns>
        /// <c>true</c> if an auto-complete occurred or if an IntelliPrompt member list is displayed; otherwise, <c>false</c>.
        /// </returns>
        public override bool IntelliPromptCompleteWord(SyntaxEditor syntaxEditor)
        {
            return this.ShowIntelliPromptMemberList(syntaxEditor, true);
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool IntelliPromptMemberListSupported
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets whether IntelliPrompt parameter info features are supported by the language.
        /// </summary>
        /// <value>
        /// <c>true</c> if IntelliPrompt parameter info features are supported by the language; otherwise, <c>false</c>.
        /// </value>
        /// <remarks>
        /// If this property is <c>true</c>, then the <see cref="ShowIntelliPromptParameterInfo"/> method may be used.
        /// </remarks>
        public override bool IntelliPromptParameterInfoSupported
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets whether IntelliPrompt quick info features are supported by the language.
        /// </summary>
        /// <value>
        /// <c>true</c> if IntelliPrompt quick info features are supported by the language; otherwise, <c>false</c>.
        /// </value>
        /// <remarks>
        /// If this property is <c>true</c>, then the <see cref="ShowIntelliPromptQuickInfo"/> method may be used.
        /// </remarks>
        public override bool IntelliPromptQuickInfoSupported
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Resets the <see cref="SyntaxLanguage.LineCommentDelimiter"/> property to its default value.
        /// </summary>
        public override void ResetLineCommentDelimiter()
        {
            this.LineCommentDelimiter = "//";
        }
        /// <summary>
        /// Indicates whether the <see cref="SyntaxLanguage.LineCommentDelimiter"/> property should be persisted.
        /// </summary>
        /// <returns>
        /// <c>true</c> if the property value has changed from its default; otherwise, <c>false</c>.
        /// </returns>
        public override bool ShouldSerializeLineCommentDelimiter()
        {
            return (this.LineCommentDelimiter != "//");
        }

        /// <summary>
        /// Gets the <see cref="IMergableLexicalParser"/> that can be used for lexical parsing of the language.
        /// </summary>
        /// <value>The <see cref="IMergableLexicalParser"/> that can be used for lexical parsing of the language.</value>
        protected override IMergableLexicalParser MergableLexicalParser
        {
            get
            {
                return lexicalParser;
            }
        }

        /// <summary>
        /// Occurs when a description tip is about to be displayed for the selected IntelliPrompt member list item,
        /// but the item has no description set.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
        /// <param name="e">An <c>EventArgs</c> that contains the event data.</param>
        protected override void OnSyntaxEditorIntelliPromptMemberListItemDescriptionRequested(SyntaxEditor syntaxEditor, EventArgs e)
        {
            // Get the selected item
            IntelliPromptMemberListItem item = syntaxEditor.IntelliPrompt.MemberList.SelectedItem;
            if (item == null)
                return;

            // Update the item description
            if (item.Tag is FunctionDeclaration)
                item.Description = this.GetQuickInfoForFunctionDeclaration((FunctionDeclaration)item.Tag, -1, false);
            else if (item.Tag is Identifier)
            {
                Identifier identifier = (Identifier)item.Tag;
                if ((identifier.ParentNode is FunctionDeclaration) && (identifier.ContextID == FunctionDeclaration.ParameterContextID))
                    item.Description = this.GetQuickInfoForFunctionParameter(identifier, false);
            }
            else if (item.ImageIndex == (int)ActiproSoftware.Products.SyntaxEditor.IconResource.Keyword)
                item.Description = this.GetQuickInfoForKeyword1(item.Text);
        }

        /// <summary>
        /// Occurs after the parameter index of the IntelliPrompt parameter info is changed while the parameter info is visible.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
        /// <param name="e">An <c>EventArgs</c> that contains the event data.</param>
        protected override void OnSyntaxEditorIntelliPromptParameterInfoParameterIndexChanged(SyntaxEditor syntaxEditor, EventArgs e)
        {
            if (this.UpdateParameterInfoSelectedText(syntaxEditor, true))
                syntaxEditor.IntelliPrompt.ParameterInfo.MeasureAndResize(syntaxEditor.IntelliPrompt.ParameterInfo.Bounds.Location);
        }

        /// <summary>
        /// Occurs before a <see cref="SyntaxEditor.KeyTyped"/> event is raised 
        /// for a <see cref="SyntaxEditor"/> that has a <see cref="Document"/> using this language.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
        /// <param name="e">An <c>KeyTypedEventArgs</c> that contains the event data.</param>
        protected override void OnSyntaxEditorKeyTyped(SyntaxEditor syntaxEditor, KeyTypedEventArgs e)
        {
            switch (e.KeyChar)
            {
                case '(':
                    if (syntaxEditor.Caret.Offset > 1)
                    {
                        // Show the parameter info for code
                        this.ShowParameterInfoCore(syntaxEditor, syntaxEditor.Caret.Offset);
                    }
                    break;
                case ')':
                    if (syntaxEditor.Caret.Offset > 1)
                    {
                        // Show the parameter info for the parent context level if there is one
                        this.ShowIntelliPromptParameterInfo(syntaxEditor);
                    }
                    break;
                case ',':
                    if ((!syntaxEditor.IntelliPrompt.ParameterInfo.Visible) && (syntaxEditor.Caret.Offset > 1) && (syntaxEditor.SelectedView.GetCurrentToken().LexicalState == this.LexicalStates["DefaultState"]))
                    {
                        // Show the parameter info for the context level if parameter info is not already displayed
                        this.ShowIntelliPromptParameterInfo(syntaxEditor);
                    }
                    break;
            }
        }

        /// <summary>
        /// Occurs when the mouse is hovered over an <see cref="EditorView"/>.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will raise the event.</param>
        /// <param name="e">An <c>EditorViewMouseEventArgs</c> that contains the event data.</param>
        protected override void OnSyntaxEditorViewMouseHover(SyntaxEditor syntaxEditor, EditorViewMouseEventArgs e)
        {
            if ((e.HitTestResult.Token == null) || (e.ToolTipText != null))
                return;

            int offset = e.HitTestResult.Token.StartOffset;
            e.ToolTipText = this.GetQuickInfo(syntaxEditor, ref offset);
        }

        /// <summary>
        /// Performs automatic outlining over the specified <see cref="TextRange"/> of the <see cref="Document"/>.
        /// </summary>
        /// <param name="document">The <see cref="Document"/> to examine.</param>
        /// <param name="parseTextRange">A <see cref="TextRange"/> indicating the offset range to parse.</param>
        /// <returns>A <see cref="TextRange"/> containing the offset range that was modified by outlining.</returns>
        public override TextRange PerformAutomaticOutlining(Document document, TextRange parseTextRange)
        {
            // If there is another pending semantic parser request (probably due to typing), assume that the existing outlining structure 
            //   in the document is more up-to-date and wait until the final request comes through before updating the outlining again
            if (!SemanticParserService.HasPendingRequest(SemanticParserServiceRequest.GetParseHashKey(document, document)))
                return new CollapsibleNodeOutliningParser().UpdateOutlining(document, parseTextRange, document.SemanticParseData as CompilationUnit);
            else
                return TextRange.Deleted;
        }

        /// <summary>
        /// Semantically parses the specified <see cref="TextRange"/> of the <see cref="Document"/>.
        /// </summary>
        /// <param name="document">The <see cref="Document"/> to examine.</param>
        /// <param name="parseTextRange">A <see cref="TextRange"/> indicating the offset range to parse.</param>
        /// <param name="flags">A <see cref="SemanticParseFlags"/> that indicates semantic parse flags.</param>
        public override void PerformSemanticParse(Document document, TextRange parseTextRange, SemanticParseFlags flags)
        {
            SemanticParserService.Parse(new SemanticParserServiceRequest(SemanticParserServiceRequest.MediumPriority,
                document, parseTextRange, flags, this, document));
        }

        /// <summary>
        /// Semantically parses the text in the <see cref="MergableLexicalParserManager"/>.
        /// </summary>
        /// <param name="manager">The <see cref="MergableLexicalParserManager"/> that is managing the mergable language and the text to parse.</param>
        /// <returns>An object that contains the results of the semantic parsing operation.</returns>
        protected override object PerformSemanticParse(MergableLexicalParserManager manager)
        {
            SimpleRecursiveDescentLexicalParser descentLexicalParser = new SimpleRecursiveDescentLexicalParser(this, manager);
            descentLexicalParser.InitializeTokens();
            SimpleSemanticParser semanticParser = new SimpleSemanticParser(descentLexicalParser);
            semanticParser.Parse();
            return semanticParser.CompilationUnit;
        }

        /// <summary>
        /// Displays an IntelliPrompt member list in a <see cref="SyntaxEditor"/> based on the current context.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt member list.</param>
        /// <returns>
        /// <c>true</c> if an IntelliPrompt member list is displayed; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        /// Only call this method if the <see cref="IntelliPromptMemberListSupported"/> property is set to <c>true</c>.
        /// </remarks>
        public override bool ShowIntelliPromptMemberList(SyntaxEditor syntaxEditor)
        {
            return this.ShowIntelliPromptMemberList(syntaxEditor, false);
        }

        /// <summary>
        /// Displays IntelliPrompt parameter info in a <see cref="SyntaxEditor"/> based on the current context.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt parameter info.</param>
        /// <returns>
        /// <c>true</c> if IntelliPrompt parameter info is displayed; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        /// Only call this method if the <see cref="IntelliPromptParameterInfoSupported"/> property is set to <c>true</c>.
        /// </remarks>
        public override bool ShowIntelliPromptParameterInfo(SyntaxEditor syntaxEditor)
        {
            // Determine the maximum number of characters to look back at
            int minOffset = Math.Max(0, syntaxEditor.Caret.Offset - 500);

            // Try and find an open '('
            int targetOffset = -1;
            TextStream stream = syntaxEditor.Document.GetTextStream(syntaxEditor.Caret.Offset);
            stream.GoToCurrentTokenStart();
            bool exitLoop = false;
            while (stream.Offset > minOffset)
            {
                IToken token = stream.ReadTokenReverse();
                switch (token.ID)
                {
                    case SimpleTokenID.CloseCurlyBrace:
                    case SimpleTokenID.CloseParenthesis:
                        stream.GoToPreviousMatchingToken(token);
                        break;
                    case SimpleTokenID.OpenParenthesis:
                        targetOffset = token.EndOffset;
                        exitLoop = true;
                        break;
                }
                if (exitLoop)
                    break;
            }

            if (targetOffset != -1)
                return this.ShowParameterInfoCore(syntaxEditor, targetOffset);
            else
                return false;
        }

        /// <summary>
        /// Displays IntelliPrompt quick info in a <see cref="SyntaxEditor"/> based on the current context.
        /// </summary>
        /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> that will display the IntelliPrompt quick info.</param>
        /// <returns>
        /// <c>true</c> if IntelliPrompt quick info is displayed; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        /// Only call this method if the <see cref="IntelliPromptQuickInfoSupported"/> property is set to <c>true</c>.
        /// </remarks>
        public override bool ShowIntelliPromptQuickInfo(SyntaxEditor syntaxEditor)
        {
            int offset = syntaxEditor.Caret.Offset;

            // Get the info for the context at the caret
            string quickInfo = this.GetQuickInfo(syntaxEditor, ref offset);

            // No info was found... try the offset right before the caret
            if (offset > 0)
            {
                offset = syntaxEditor.Caret.Offset - 1;
                quickInfo = this.GetQuickInfo(syntaxEditor, ref offset);
            }

            // Show the quick info if there is any
            if (quickInfo != null)
            {
                syntaxEditor.IntelliPrompt.QuickInfo.Show(offset, quickInfo);
                return true;
            }

            return false;
        }
    }
}
